﻿using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Mapper;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Utils;
using Mysqlx.Session;
using NPOI.SS.Formula.Eval;
using S7.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Joins;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace DeHeng_Mes.Thread
{

    public class CodeDataCollect
    {
        #region 成员实例
        PlcDao plcDao = new PlcDao();
        CodeTraceDao codeTraceDao = new CodeTraceDao();
        DataCollector collector = new DataCollector();
        Dictionary<string, Dictionary<string, bool>> saveStatus = StationSaveStatusDic.SaveStatus;
        #endregion

        /// <summary>
        /// 前围自动线工序数据采集
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        public void FrontCodeDataCollecting(Dictionary<object, Type> types, PlcOrderConfigModel orderModel, List<string> operations, Dictionary<string, bool> statusDictionary)
        {
            try
            {
                // 正则匹配
                Regex regex = new Regex(@"Glue\d+");

                foreach (var operation in operations)
                {

                    // 转换为程序可接受的工序号
                    (string operationReal, bool isPut) = MappingTable.GetOperation(operation, orderModel.PlcName);

                    Dictionary<string, object> dictionary = plcDao.GetFieldValues(types, SystemConstant.StationStatus);

                    // 根据工序获取状态值
                    bool readResult = statusDictionary[operationReal];

                    // 初始化变量
                    string traceCode = ""; // 总成追溯码

                    string beat = ""; // 节拍

                    bool operationStatus = false; // 工序状态

                    if (!readResult)
                    {
                        // 报工工序处理
                        if (isPut)
                        {

                            // 拼接转台标识字符
                            operationReal = operationReal + "_A";

                            // 判断是否转台AB面是否有存在完成的状态
                            if (!plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal))
                            {
                                operationReal = operationReal.Replace("_A", "_B");

                                if (!plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal))
                                    continue;
                            }

                            // 获取总成码
                            traceCode = plcDao.GetStringFieldValue(types, SystemConstant.Code, operationReal);

                            // 获取工序结果
                            operationStatus = plcDao.GetBoolFieldValue(types, SystemConstant.ProduceStatus, operationReal);

                            // 获取节拍
                            beat = plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal);

                            // 推送数据至工厂Mes
                            OperationPutFactoryMae(types, orderModel, operationStatus, operationReal, traceCode, operation);



                        }
                        else if (plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal))
                        {

                            // 获取总成码
                            traceCode = plcDao.GetStringFieldValue(types, SystemConstant.Code, operationReal);

                            // 判断是否是下线工序
                            if (operationReal.Contains("Vision"))
                            {
                                // 下线工序，绑定工单 => 存一级码表
                                if (!codeTraceDao.IsBanding(orderModel.orderPlan.OrderId, traceCode))
                                    codeTraceDao.BandingOrderId(orderModel.orderPlan.OrderId, traceCode);

                                // 推送下线产量
                                LineProcessPut(types, orderModel);
                            }

                            // 设置已读状态
                            saveStatus[orderModel.orderPlan.OrderId][operationReal] = true;

                            // 获取工序结果
                            operationStatus = plcDao.GetBoolFieldValue(types, SystemConstant.ProduceStatus, operationReal);

                            // 获取节拍
                            beat = plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal);

                            //获取该工序对应的偏移量
                            (int startByteAdr, int bitAdr) = MappingTable.OperationGetOff(orderModel.PlcName, operationReal);
                            orderModel.plc.WriteBit(DataType.DataBlock, SystemConstant.Write, startByteAdr, bitAdr, true);
                        }

                        // 绑定附属码和追溯码 => 二级码表处理
                        BindSubsidiaryCodes(types, operationReal, traceCode, orderModel);

                        // 绑定追溯码与工序、节拍数据、工序结果
                        BindBeatData(types, traceCode, operationReal, beat, operationStatus, orderModel);

                        // 处理涂胶数据
                        if (operation.Contains("Glue"))
                        {
                            _ = Task.Run(() => collector.CollectDataForGluing(DateTime.Now, orderModel.orderPlan.OrderId, traceCode, operation));
                        }
                        else if (operationReal.Contains("Glue"))
                        {
                            _ = Task.Run(() => collector.CollectDataForGluing(DateTime.Now, orderModel.orderPlan.OrderId, traceCode, operationReal));
                        }

                        //获取该工序的状态，如果是Ng状态则插入Ng工序的相关数据
                        if (!operationStatus)
                        {
                            if (codeTraceDao.PadCodeIsSave(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime"))))
                                // 插入Ng工序的条码等数据，暂存数据库提供给工厂Mes接口
                                codeTraceDao.InsertPadTrace(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime")));
                        }

                        // 判断是否是视觉工序如果是视觉工序则绑定视觉码与追溯码
                        if (MappingTable.GetVisionOperation(orderModel.PlcName, operationReal))
                        {
                            // 获取视觉码
                            string visionCode = plcDao.GetStringFieldValue(types, SystemConstant.IssuedCode, "Code7");

                            // 绑定视觉码与追溯码
                            BindVisionCode(orderModel, visionCode, traceCode);
                        }
                    }
                    else
                    {
                        saveStatus[orderModel.orderPlan.OrderId][operationReal] = plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal);
                    }
                }
                GlueCodeBanding(types, regex, "Glue1", true);
            }
            catch (Exception e)
            {
                Tools.ProduceLogTextError($"前围总成自动线数据处理失败，失败原因是{e.Message}");
            }
        }

        /// <summary>
        /// 前围分装线数据采集
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        /// <param name="operations"></param>
        /// <param name="statusDictionary"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void SubCodeDataCollecting(Dictionary<object, Type> types, PlcOrderConfigModel orderModel, List<string> operations, Dictionary<string, bool> statusDictionary)
        {
            try
            {
                Dictionary<string, object> dictionary = plcDao.GetFieldValues(types, SystemConstant.StationStatus);

                // 正则匹配
                Regex regex = new Regex(@"Op10Glue\d+");

                // 遍历所有工序
                foreach (var operation in operations)
                {
                    // 替换可以接受的工序号
                    (string operationReal, bool isPut) = MappingTable.GetOperation(operation, orderModel.PlcName);

                    // 根据工序获取状态值
                    bool readResult = statusDictionary[operationReal];

                    // 初始化变量
                    string traceCode = ""; // 总成追溯码

                    string beat = ""; // 节拍

                    bool operationStatus = false; // 工序状态

                    // 获取虚拟码
                    string code = plcDao.GetStringFieldValue(types, SystemConstant.Code, operationReal + "_1");

                    // 判断工序是否是完成状态
                    if (plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal) && !readResult)
                    {
                        // 获取追溯码
                        traceCode = plcDao.GetStringFieldValue(types, SystemConstant.Code, operationReal);

                        // 获取节拍
                        beat = plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal);

                        // 获取工序状态
                        operationStatus = plcDao.GetBoolFieldValue(types, SystemConstant.ProduceStatus, operationReal);

                        // 绑定工单
                        BindTraceCode(orderModel.orderPlan.OrderId, traceCode, code);

                        // 绑定追溯码与工序、节拍数据、工序结果
                        BindBeatData(types, traceCode, operationReal, beat, operationStatus, orderModel);

                        // 判断是否是下线工序，如果是下线工序那么向工厂Mes推送产量分析数据
                        if (MappingTable.GetLinePart(orderModel.PlcName, operationReal))
                            LineProcessPut(types, orderModel);

                        // 工序报工
                        if (orderModel.orderPlan.ProcessCodes.Contains(operation))
                        {
                            OperationPutFactoryMae(types, orderModel, operationStatus, operationReal, traceCode, operation);
                        }

                        // 处理涂胶数据
                        if (operation.Contains("Glue"))
                        {
                            _ = Task.Run(() => collector.CollectDataForGluing(DateTime.Now, orderModel.orderPlan.OrderId, traceCode, operation));
                        }
                        else if (operationReal.Contains("Glue"))
                        {
                            _ = Task.Run(() => collector.CollectDataForGluing(DateTime.Now, orderModel.orderPlan.OrderId, traceCode, operationReal));
                        }

                        //获取该工序的状态，如果是Ng状态则插入Ng工序的相关数据
                        if (!operationStatus)
                        {
                            if (codeTraceDao.PadCodeIsSave(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime"))))
                                // 插入Ng工序的条码等数据，暂存数据库提供给工厂Mes接口
                                codeTraceDao.InsertPadTrace(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime")));
                        }

                    }
                    else
                    {
                        saveStatus[orderModel.orderPlan.OrderId][operationReal] = plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal);
                    }
                }

                GlueCodeBanding(types, regex, "Op10Glue", false);
            }
            catch (Exception e)
            {
                Tools.ProduceLogText($"前围分装线数据处理失败，失败原因是{e.Message}");
            }
        }

        /// <summary>
        /// 绑定三轴涂胶数据
        /// </summary>
        /// <param name="operationReal"></param>
        private void GlueCodeBanding(Dictionary<object, Type> types, Regex regex, string operationReal, bool isAuto)
        {
            // 匹配三轴数据进行处理
            if (regex.IsMatch(operationReal) && plcDao.GetBoolFieldValue(types, SystemConstant.StationStatus, operationReal))
            {
                // 获取节拍
                string beat = plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal);

                List<string> codes = new List<string>()
                {
                    plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal + "_1")
                };

                if (isAuto)
                {
                    codes.Add(plcDao.GetStringFieldValue(types, SystemConstant.Beat, operationReal + "_2"));
                }

                foreach (var code in codes)
                {
                    if (!codeTraceDao.GlueCodeIsSave(beat, code, operationReal))
                        codeTraceDao.GlueCodeSave(beat, code, operationReal);
                }
            }
        }

        /// <summary>
        /// 绑定追溯码与视觉码
        /// </summary>
        /// <param name="orderModel"></param>
        /// <param name="visionCode"></param>
        /// <param name="traceCode"></param>
        private void BindVisionCode(PlcOrderConfigModel orderModel, string visionCode, string traceCode)
        {
            if (codeTraceDao.BandingVisionCode(visionCode, traceCode))
                Tools.ProduceLogText($"成功插入一条视觉码与追溯码，视觉码为{visionCode}，追溯码为{traceCode}");
        }

        /// <summary>
        /// 工件下线推送总产量数据
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        private async void LineProcessPut(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            // 获取需要推送的产量数据
            string produceNg = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOrder, "ProduceAll_Ng" + orderModel.OrderNumInPlc);  //Ng总产量

            string produce = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOrder, "ProduceAll" + orderModel.OrderNumInPlc);  //总产量

            // 构建数据推送
            BossMessager bossMessager = await PutMes.LineMesToMes(new BossProducePut()
            {
                OrderId = orderModel.orderPlan.OrderId,
                Produce = decimal.Parse(produce),
                ProduceNg = decimal.Parse(produceNg)
            }, SystemConstant.BossProducePutUrl, $"工件下线任务开始推送，工单号为：{orderModel.orderPlan.OrderId}，当前产量为{produce}，当前Ng产量为{produceNg}，任务推送");
        }

        /// <summary>
        /// 绑定单站类型的数据
        /// </summary>
        /// <param name="orderId"></param>
        /// <param name="traceCode"></param>
        /// <param name="code"></param>
        private void BindTraceCode(string orderId, string traceCode, string code)
        {
            // 防错，不存在即插入
            if (!codeTraceDao.IsBandingOne(orderId, traceCode))
                codeTraceDao.BandingOrderId(orderId, traceCode, code);
            // 检查缓存表是否插入
            if (!codeTraceDao.CacheIsInsert(orderId, traceCode, code))
                codeTraceDao.InsertAllToCacheTable(orderId, traceCode, code); // 插入缓存表
        }

        /// <summary>
        /// 工序报工通用方法
        /// </summary>
        /// <param name="types"></param>
        /// <param name="orderModel"></param>
        /// <param name="operationStatus"></param>
        /// <param name="operationReal"></param>
        /// <param name="traceCode"></param>
        /// <param name="operation"></param>
        private async void OperationPutFactoryMae(Dictionary<object, Type> types, PlcOrderConfigModel orderModel, bool operationStatus, string operationReal, string traceCode, string operation)
        {

            //获取该工序的状态，如果是Ng状态则插入Ng工序的相关数据
            if (!operationStatus)
            {
                // 判断当前数据是否已经插入
                if (!codeTraceDao.PadTraceIsSave(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime"))))
                    // 插入Ng工序的条码等数据，暂存数据库提供给工厂Mes接口
                    codeTraceDao.InsertPadTrace(orderModel.orderPlan.OrderId, traceCode, operation, Tools.GetTimeRangeFromDateTime(plcDao.GetStringFieldValue(types, SystemConstant.Collection, "CreateTime")));
            }

            /*Dictionary<string, object> dictionary = plcDao.GetFieldValues(types,SystemConstant.ProduceOperation);

            string v = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, "All_" + operationReal);
            string v1 = plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, "All_" + operationReal + "Ng");*/
            // 包装推送数据
            BossProcess bossProcess = new BossProcess()
            {
                OrderId = orderModel.orderPlan.OrderId,
                ProcessId = operation,
                ProduceAll = decimal.Parse(plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, "All_" + operationReal)),
                ProduceNg = decimal.Parse(plcDao.GetStringFieldValue(types, SystemConstant.ProduceOperation, "All_" + operationReal + "Ng"))
            };

            //调用接口推送数据
            BossMessager bossMessager = await PutMes.LineMesToMes(bossProcess, SystemConstant.ProduceOperationUrl, $"工序报工，工单号为：{orderModel.orderPlan.OrderId}，工序号为：{operation}，" +
                $"该工序总产量为：{bossProcess.ProduceAll}，该工序Ng产量为：{bossProcess.ProduceNg}，任务推送");
            if (bossMessager != null && (bossMessager.code == 200/*||bossMessager.*/))
            {
                //获取该工序对应的偏移量
                (int startByteAdr, int bitAdr) = MappingTable.OperationGetOff(orderModel.PlcName, operationReal);
                orderModel.plc.WriteBit(DataType.DataBlock, SystemConstant.Write, startByteAdr, bitAdr, true);

                saveStatus[orderModel.orderPlan.OrderId][operationReal] = true;
            }
        }

        /// <summary>
        /// 绑定附属码和追溯码
        /// </summary>
        private void BindSubsidiaryCodes(Dictionary<object, Type> types, string operation, string traceCode, PlcOrderConfigModel orderModel)
        {
            string codes = "";
            try
            {
                var codeDic = plcDao.GetFieldValues(types, SystemConstant.Code);
                var subsidiaryCodes = codeDic.Where(item => item.Key.Contains(operation + "_")).Select(item => item.Value.ToString()).Where(value => !string.IsNullOrEmpty(value)).ToList();

                foreach (var code in subsidiaryCodes)
                {
                    codes = code + "、" + codes;
                    if (!codeTraceDao.IsBandingTraceId(code, traceCode))
                    {
                        if (codeTraceDao.BandingTraceId(code, traceCode))
                        {
                            Tools.ProduceLogText($"{orderModel.orderPlan.OrderId}成功绑定一条追溯码与附属码，追溯码为：{traceCode}，附属码为：{code}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.orderPlan.OrderId}绑定附属码和追溯码失败，追溯码为：{traceCode}，附属码为：{codes}，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 绑定追溯码与节拍数据
        /// </summary>
        private bool BindBeatData(Dictionary<object, Type> types, string traceCode, string operation, string beat, bool operationStatus, PlcOrderConfigModel orderModel)
        {
            try
            {
                if (!codeTraceDao.IsBandBeat(traceCode, operation))
                {
                    return codeTraceDao.BandingBeat(traceCode, operation, beat, operationStatus);
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.orderPlan.LineId}绑定追溯码与节拍数据失败，追溯码为：{traceCode}，工序号为：{operation}，节拍为：{beat}，原因是：" + ex.Message);
            }
            return false;
        }
    }
}
